/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
 *
 */

import crypto, { Hash } from 'crypto';
import path from 'path';

import {
  coreParameter, ExtendedErrorInfo,
  FileSet,
  getWorkerIdWithModule,
  HvigorConfigLoader,
  hvigorCore,
  isMac,
  isWindows,
  PoolConstant,
  ProjectCacheService,
  setWorkerIdWithModule,
  TaskDetails,
  TaskInputValue,
} from '@ohos/hvigor';
import { MSGPACK_COMPILE_CACHE_DIR_NAME, Obfuscation, ObfuscationOptions, PkgBriefInfo, ProjectConfig } from '@ohos/hvigor-arkts-compose';
import * as fse from 'fs-extra';
import { getUrl, URL_KEY_ENUM } from '@ohos/hvigor-logger';
import { getOsLanguage } from '@ohos/hvigor-common';

import { ohosTrace } from '../common/trace/ohos-trace.js';
import { BuildArtifactConst, BuildDirConst } from '../const/build-directory-const.js';
import { ArkPackConst, CommonConst, DefaultTargetConst, ParameterizationConst } from '../const/common-const.js';
import { InjectConst } from '../const/inject-const.js';
import { ApiVersion } from '../const/sdk-const.js';
import { CodeType } from '../enum/code-type-enum.js';
import { TargetSourceSetModel } from '../model/source-set/source-set-model.js';
import { BuildOpt } from '../options/build/build-opt.js';
import { RouterMapOptions } from '../options/configure/router-map-options.js';
import { DependencyManager } from '../project/dependency/dependency-manager.js';
import { getObfuscationOptionsWithCache } from '../common/obfuscation/get-obfuscation-rules.js';
import { HarTargetUtil } from '../utils/har-target-util.js';
import { InjectUtil } from '../utils/inject-util.js';
import { getJson5Obj } from '../utils/json-util.js';
import { OhosLogger } from '../utils/log/ohos-logger.js';
import { mod } from '../utils/math-util.js';
import { getMockConfigKey2ModuleInfoMap, getMockConfigSources } from '../utils/mock-config-utils.js';
import { resolveObfuscationOptions } from '../utils/obfuscation-config-resolver.js';
import { getDependenciesPkgBriefInfo } from '../utils/source-roots-utils.js';
import { limitMinApiVersion, shouldCompileArkTsWidget, warnCreateBuildProfileTask } from '../utils/task-util.js';
import { AbstractCompileNode } from './abstract/abstract-compile-node.js';
import { TaskNames } from './common/task-names.js';
import { CompileResource } from './compile-resource.js';
import { GenerateLoaderJson } from './generate-loader-json.js';
import { ArtifactType } from './har/har-extend-info.js';
import { GlobalProjectDataService } from './service/global-project-data-service.js';
import { TargetTaskService } from './service/target-task-service.js';
import { submitArkCompileWork } from './worker/run-ark.js';

import CommonTask = TaskNames.CommonTask;
import RouterMapObj = RouterMapOptions.RouterMapObj;
import { getLocalDependencyRouterMap, getLocalDependencyBuildOptions } from '../utils/dependency-util.js';
import { CreateModuleInfo } from './module-info/create-module-info.js';
import { getOhosUiTransformOptimization } from './module-info/ohos-uitransform-optimization.js';
import { getByteCodeHarToDependencyKeys } from '../utils/byte-code-har-utils.js';
import { Dependency } from '../project/dependency/core/dependency-interface.js';
import { getCustomTypePathIfExists } from '../utils/build-profile-utils.js';
import { ohPackageLoader } from '../utils/loader/file/oh-package-loader.js';
import { FileUtil } from '../utils/file-util.js';
import { pathParamsOpt } from '../validate/dependency-tag-validate.js';

export class ArkCompile extends AbstractCompileNode {
  protected _log: OhosLogger = OhosLogger.getLogger(ArkCompile.name);
  protected readonly needSubmitArkTsWidget: boolean;
  private readonly formJsonPathArr: string[];
  protected obfuscationOptions?: ObfuscationOptions;
  protected obfuscationHash: string = '';
  protected pkgNameToPkgBriefInfo: Record<string, PkgBriefInfo>;
  private readonly _taskService: TargetTaskService;
  private arkdataPathAll: string[];
  private byteCodeHarToDependencyKeys?: Map<string, string[]>;
  private localModuleParamMap: Map<string, string> = new Map<string, string>();

  constructor(taskService: TargetTaskService, codeType: CodeType, taskDetails: TaskDetails) {
    super(taskService, codeType, taskDetails);
    const targetSourceSetModel: TargetSourceSetModel = taskService.getTargetData().getModuleSourceSetModel() as TargetSourceSetModel;
    const targetRes = targetSourceSetModel.getModuleTargetRes();
    this.formJsonPathArr = TargetTaskService.getFormJsonArr(targetRes);
    this.needSubmitArkTsWidget = shouldCompileArkTsWidget(this.formJsonPathArr);
    this.pkgNameToPkgBriefInfo = {};
    this._taskService = taskService;
    this.arkdataPathAll = [];
  }

  taskShouldDo(): boolean {
    return (this.sourcePath !== undefined || this.checkPackageMain()) && this.checkRollUpPlugin();
  }

  declareInputs(): Map<string, TaskInputValue> {
    const inputs: Map<string, TaskInputValue> = super.declareInputs().set('arkTsWdiget', this.needSubmitArkTsWidget);
    const debugLine = hvigorCore.getExtraConfig().get(CommonConst.DEBUG_LINE);
    if (debugLine) {
      inputs.set(CommonConst.DEBUG_LINE, debugLine);
    }
    const enable: boolean | undefined = this.getObfuscationEnable();
    inputs.set(CommonConst.OBFUSCATION_ENABLE, String(enable));
    inputs.set(CommonConst.OBFUSCATION_FILES_HASH, this.obfuscationHash);
    // tsc编译的控制流最大递归深度
    inputs.set(CommonConst.TSC_MAX_FLOW_DEPTH, String(this.buildOption?.arkOptions?.tscConfig?.maxFlowDepth));

    inputs.set(CommonConst.NO_EXTERNAL_IMPORT_BY_PATH, this.targetService.isNoExternalImportByPath() ?? false);

    // 增加增量判断
    inputs.set(CommonConst.COPY_CODE_RESOURCE_ENABLE, this.targetService.getBuildOption()?.resOptions?.copyCodeResource?.enable ?? true);

    if (this.targetService.getBuildOption()?.resOptions?.copyCodeResource?.enable ?? true) {
      inputs.set(CommonConst.COPY_CODE_RESOURCE_EXCLUDES, this.targetService.getBuildOption()?.resOptions?.copyCodeResource?.excludes ?? []);
    }

    inputs.set(CommonConst.OHOS_UI_TRANSFORM_OPTIMIZATION, getOhosUiTransformOptimization(this.targetService));
    inputs.set(CommonConst.OHOS_ROLLUP_CACHE_USE_SOURCE_HASH, HvigorConfigLoader.getInstance().getPropertiesConfigValue(CommonConst.OHOS_ROLLUP_CACHE_USE_SOURCE_HASH) === true,)

    inputs.set('localModuleParamMap', JSON.stringify(Object.fromEntries(this.localModuleParamMap)));
    return inputs;
  }

  declareOutputFiles(): FileSet {
    const fileSet = super.declareOutputFiles();
    const targetName = this.targetService.getTargetData().getTargetName();

    if (
      this.moduleModel.isHspModule() &&
      targetName !== DefaultTargetConst.OHOS_TEST_TARGET &&
      !InjectUtil.isColdReload() &&
      !InjectUtil.isUnitTestProcess() &&
      !InjectUtil.isHotReload() &&
      !InjectUtil.isPreviewProcess()
    ) {
      fileSet.addEntry(path.resolve(this.aceModuleBuild, `../${BuildDirConst.DECLARE_FILE_OUTPUT}`), { isDirectory: true });
    }

    return fileSet;
  }

  private addRootForDependencyCustomTypes(dependency: Dependency, customTypes: string[]): string[] {
    const result: string[] = [];
    const pkgRoot = dependency.getDependencyRootPath();
    const pkgJson = dependency.getPackageJsonObj() as any;
    const keys = Object.keys({
      ...(pkgJson?.devDependencies || {}),
      ...(pkgJson?.dependencies || {})
    });
    const warningTypes: string[] = [];
    const selfCustomTypes = this.targetService.getCustomTypes();
    customTypes.forEach(type => {
      // 三方包
      const isConfigured = ([type, `@types/${type}`]).some(v => keys.includes(v));
      if (isConfigured) {
        if (!selfCustomTypes?.includes(type)) {
          warningTypes.push(type);
        }
        return;
      }
      // 路径
      const isExists = !!getCustomTypePathIfExists(pkgRoot, type);
      if (!isExists) {
        warningTypes.push(type);
        return;
      }
      result.push(path.isAbsolute(type) ? type : path.resolve(pkgRoot, type));
    });
    if (warningTypes.length) {
      const country = getOsLanguage();
      const host = getUrl(URL_KEY_ENUM.DEVELOPER_URL);
      this._log.warn(
        `Can not find custom types: '[${warningTypes.join(',')}]', try to configure at file: ${this.service.getProjectModel().getProfilePath()}. 
         Reference: ${host}/consumer/${country}/doc/harmonyos-guides/arkoptions-guide#types`)
      ;
    }
    return result;
  }

  private getDependencyCustomTypes(dependency: Dependency, moduleName2TargetName: Map<string, string>): string[] {
    let customTypes: string[] = [];
    if (dependency.isLocal()) {
      const buildOpt = getLocalDependencyBuildOptions(dependency, moduleName2TargetName);
      customTypes = (buildOpt.arkOptions?.types || []) as string[];
    } else {
      const pkgJson = dependency.getPackageJsonObj() as any;
      customTypes = (pkgJson.metadata?.customTypes || []) as string[];
    }
    return this.addRootForDependencyCustomTypes(dependency, customTypes);
  }

  protected collectCustomTypes(): string[] {
    const moduleName2TargetName = HarTargetUtil.calDepHarTargets(this.targetService);
    const customTypes = [...(this.targetService.getCustomTypes() || [])];
    this.service.getHarDependencies().forEach(dependency => {
      const dependencyCustomTypes = this.getDependencyCustomTypes(dependency, moduleName2TargetName);
      if (dependencyCustomTypes.length) {
        customTypes.push(...dependencyCustomTypes);
      }
    });
    this._log.debug(`All custom types found in module '${this.moduleName}': ${JSON.stringify(customTypes)}`);
    return customTypes;
  }

  async beforeAlwaysAction() {
    await super.beforeAlwaysAction();
    // 增量计算之前获取混淆配置, 且可避免多次计算
    this.obfuscationOptions = await this.getObfuscationOptions();
    if (this.obfuscationOptions) {
      this.obfuscationHash = this.computeObfuscationFileHash(this.obfuscationOptions);
    }
    this._log.debug(`obfuscationOptions: ${JSON.stringify(this.obfuscationOptions)}`);

    // arkData的har模块依赖编译
    this.arkDataCompile();
    this.setPkgNameToPkgBriefInfo();
    this.setByteCodeHarToDependencyKeys();
    this.setLocalModuleParamMap();
  }

  private setByteCodeHarToDependencyKeys() {
    // localTest | ohosTest
    const isTestMode = InjectUtil.isInTest();
    const isObfEnabled = !!this.obfuscationOptions;
    const isUseNormalizedOHMUrlEnabled = this.getUseNormalizedOHMUrl();
    const isWatchMode = InjectUtil.isColdReload() || InjectUtil.isHotReload() || InjectUtil.isPreviewProcess();
    if (!isObfEnabled || !isUseNormalizedOHMUrlEnabled || this.moduleModel.isHarModule() || isTestMode || isWatchMode) {
      return;
    }
    this.byteCodeHarToDependencyKeys = getByteCodeHarToDependencyKeys(this.targetService);
  }

  //  获取本地依赖version和pkgPath的映射map 在此处替换@param关键字
  private setLocalModuleParamMap() {
    this.projectModel.getProject().getAllSubModules().forEach((module) => {
      const ohPackagePath = path.resolve(module.getNodeDir(), CommonConst.OH_PACKAGE_JSON5);
      const modulePackageJsonObj = ohPackageLoader.getOhPackageJsonObj(ohPackagePath);
      const pathParamsObj: pathParamsOpt = {
        parameterFilePath: this.projectModel.getParameterFileAbsolutePath(),
        ohPackageFilePath: ohPackagePath,
      };
      if(modulePackageJsonObj?.version && modulePackageJsonObj?.version.match(ParameterizationConst.REGEX)) {
        const version = FileUtil.extracted(modulePackageJsonObj?.version, this.projectModel.getParameterFileObj(), pathParamsObj);
        this.localModuleParamMap?.set(module.getNodeDir(), version);
      }
    });
  }

  protected async beforeTask(): Promise<void> {
    // 增量失效, 检查增量失效是否是因为混淆文件修改
    // 如果是因为混淆文件修改, 需要删除缓存
    await super.beforeTask();

    if (this.targetService.isDebug()) {
      return;
    }
    const obfuscationEnabled = this.getIncrementalCacheItem(CommonConst.OBFUSCATION_ENABLE);
    if (String(obfuscationEnabled) !== String(this.getObfuscationEnable())) {
      // clean cache
      this._log.debug('Clean the arkts cache due to obfuscation config changes.');
      await this.cleanCache();
      return;
    }
    const cacheObfuscationHash = this.getIncrementalCacheItem(CommonConst.OBFUSCATION_FILES_HASH);
    if (cacheObfuscationHash !== this.obfuscationHash) {
      // clean cache
      this._log.debug('Clean the arkts cache due to obfuscation file changes.');
      await this.cleanCache();
    }
  }

  declareInputFiles(): FileSet {
    const fileSet = super.declareInputFiles().addEntries(this.formJsonPathArr);

    // 获取当前模块的buildProfile.ets文件 如果存在添加增量判断
    const targetName = this.targetService.getTargetData().getTargetName();
    const buildProfilePath = this.pathInfo.getGenerateBuildProfilePath(targetName);
    if (fse.existsSync(buildProfilePath)) {
      fileSet.addEntry(buildProfilePath);
    }

    const modulePath = this.targetService.getModuleService().getModuleModel().getProjectDir();
    const cppTypesPath = path.resolve(modulePath, BuildDirConst.SRC, BuildDirConst.MAIN, BuildDirConst.CPP_TYPES);
    if (fse.existsSync(cppTypesPath)) {
      fileSet.addEntry(cppTypesPath, { isDirectory: true });
    }

    this.service.getHarModuleDependencies().forEach(([, dependency]) => {
      const harModulePath = dependency.getDependencyRootPath();
      const harModuleCppTypesPath = path.resolve(harModulePath, BuildDirConst.SRC, BuildDirConst.MAIN, BuildDirConst.CPP_TYPES);
      if (fse.existsSync(harModuleCppTypesPath)) {
        fileSet.addEntry(harModuleCppTypesPath, { isDirectory: true });
      }

      // 如果hap/hsp引用了本地har模块则对har模块的BuildProfile.ets文件进行增量判断
      const harBuildProfileEtsPath = path.resolve(harModulePath, BuildArtifactConst.BUILD_PROFILE_FILE);
      if (fse.existsSync(harBuildProfileEtsPath)) {
        fileSet.addEntry(harBuildProfileEtsPath);
      }

      // 如果hap/hsp引用了本地har模块，则har的对应target的sourceRoots需要加入增量判断
      const sourceDirs = this.pkgNameToPkgBriefInfo[dependency.getPackageName()]?.originalSourceRoots;
      if (!sourceDirs?.length) {
        return;
      }
      sourceDirs.forEach((dir) => {
        const sourceDir = path.resolve(harModulePath, dir);
        if (fse.existsSync(sourceDir)) {
          fileSet.addEntry(path.resolve(harModulePath, dir), { isDirectory: true });
        }
      });
    });

    // 自己的差异化的sourceRoot需要加入增量判断
    const sourceDirs = this.targetService.getTargetData().getTargetOpt()?.source?.sourceRoots;
    if (sourceDirs?.length) {
      sourceDirs.forEach((dir) => {
        fileSet.addEntry(path.resolve(this.pathInfo.getModulePath(), dir), { isDirectory: true });
      });
    }

    const mockConfigPath = this.moduleModel.getMockConfigPath();
    if (fse.existsSync(mockConfigPath)) {
      fileSet.addEntry(mockConfigPath, { isDirectory: false });
      fileSet.addEntries(getMockConfigSources(mockConfigPath, this.moduleModel.getProjectDir()));
    }
    const compilePluginPath = this.moduleModel.getCompilePluginPath();
    if (compilePluginPath && fse.existsSync(compilePluginPath)) {
      fileSet.addEntry(compilePluginPath);
    }
    return fileSet;
  }

  protected async doTaskAction(): Promise<void> {
    this.validateModuleJson(this._log);
    const config: ProjectConfig = await this.initDefaultArkCompileConfig();
    this._log.debug('build config:');
    this._log.anonymizeDebug(config);
    if (config.externalApiPaths.length > 0) {
      this._log.debug(`Compile arkts with external api path: ${config.externalApiPaths.join(path.delimiter)}`);
    }
    if (config.obfuscationOptions) {
      await fse.ensureDir(config.obfuscationOptions.obfuscationCacheDir);
      await fse.ensureDir(path.dirname(config.obfuscationOptions.exportRulePath));
    }
    config.cachePath && this.trace(config.cachePath);
    fse.ensureDirSync(config.cachePath!);
    fse.ensureDirSync(config.aceModuleBuild!);
    let workId: string | null = null;
    let ohosArkCompileMaxSize = coreParameter.properties.ohosArkCompileMaxSize;
    if (ohosArkCompileMaxSize === undefined || ohosArkCompileMaxSize <= 0 || ohosArkCompileMaxSize > PoolConstant.MAX_POOL_NUM) {
      ohosArkCompileMaxSize = ArkPackConst.MAX_ARK_COMPILE_NUM;
    }
    const defaultWorkers = [...new Array(ohosArkCompileMaxSize).keys()];
    let targetWorkers = defaultWorkers;
    const moduleWorkerId: number | undefined = getWorkerIdWithModule(config.modulePath);
    const maxPoolNum = this.getWorkerPool().getMaxPoolNum();
    if (moduleWorkerId !== undefined && maxPoolNum > 0) {
      targetWorkers = [mod(moduleWorkerId!, Math.min(maxPoolNum, ohosArkCompileMaxSize))];
    }
    workId = await submitArkCompileWork(
      this,
      this.getWorkerPool(),
      this._log,
      config,
      (compileEvents: object[], workerId?: number) => {
        if (workerId !== undefined) {
          setWorkerIdWithModule(config.modulePath, workerId);
        }
        this.moveReleaseMap(this.targetData, this._log, this.codeType);
        this.handleCompileEvents(compileEvents, workId ? this.getSubDurationEvent(workId) : this.durationEvent, false);
      },
      targetWorkers,
    );

    if (this.needSubmitArkTsWidget) {
      const widgetConfig: ProjectConfig = await this.initWidgetDefaultArkCompileConfig();
      let wgtTargetWorkers = defaultWorkers;
      const wgtModuleWorkerId: number | undefined = getWorkerIdWithModule(config.modulePath, true);
      if (wgtModuleWorkerId !== undefined && maxPoolNum > 0) {
        wgtTargetWorkers = [mod(wgtModuleWorkerId!, Math.min(maxPoolNum, ohosArkCompileMaxSize))];
      }

      // 目前modules.abc内会包含卡片的内容，所以卡片部分被编了两次，可能会产生重复的告警信息
      // 暂时向ets-loader传递一个ignoreWarning标识符，使得第二次编译卡片时不输出告警信息，待实现卡片热重载需求时消除卡片重复编译的问题
      (widgetConfig as ProjectConfig & { ignoreWarning: boolean }).ignoreWarning = true;
      let widgetWorkId: string | null = null;
      widgetWorkId = await submitArkCompileWork(
        this,
        this.getWorkerPool(),
        this._log,
        widgetConfig,
        (compileEvents: object[], workerId?: number) => {
          if (workerId !== undefined) {
            setWorkerIdWithModule(config.modulePath, workerId, true);
          }
          this.handleCompileEvents(compileEvents, widgetWorkId ? this.getSubDurationEvent(widgetWorkId) : this.durationEvent, false);
        },
        wgtTargetWorkers,
      );
    }
    warnCreateBuildProfileTask(this.targetData, this.targetService, this._log);
  }

  trace(cachePath: string) {
    const cacheFilePath = path.resolve(cachePath, MSGPACK_COMPILE_CACHE_DIR_NAME);
    ohosTrace.traceIncrement(this.moduleName, 'COMPILE_ARKTS', fse.existsSync(cacheFilePath));
  }

  private getSelfPkgBriefInfo() {
    const sourceRoots = this.targetData.getTargetOpt().source?.sourceRoots;
    const pkgRoot = this.pathInfo.getModulePath();
    const pkgName = this.packageJsonObj.name || this.moduleName;
    const pkgInfo: PkgBriefInfo = {
      pkgRoot,
      originalSourceRoots: sourceRoots,
      sourceRoots: [
        ...(sourceRoots || []),
        path.relative(pkgRoot, this.pathInfo.getModuleSrcMainPath()),
        path.relative(pkgRoot, path.resolve(this.pathInfo.getGenerateBuildProfileDir(), this.targetName)),
      ],
      pkgName,
    };
    return { [pkgName]: pkgInfo };
  }

  /**
   * 设置 moduleName -> 模块信息的映射对象，后续用于node-resolve插件
   * @private
   */
  private setPkgNameToPkgBriefInfo() {
    this.pkgNameToPkgBriefInfo = {
      ...getDependenciesPkgBriefInfo(this.targetService),
      ...this.getSelfPkgBriefInfo(),
    };
  }

  /**
   * 获取冷重载的rollup插件参数
   * @returns {undefined | {buildConfigJsonPath: string, relativeDir: string}}
   * @private
   */
  private getCollectImportersConfig() {
    const buildConfigPath = this.pathInfo.getBuildConfigPath();
    const buildConfig = getJson5Obj(buildConfigPath);
    if (buildConfig?.patchConfig?.mode === 'hotReload' || buildConfig?.patchConfig?.mode === 'coldReload') {
      return {
        relativeDir: path.resolve(this.pathInfo.getModuleSrcMainPath(), 'ets'),
        buildConfigJsonPath: this.pathInfo.getBuildConfigPath(),
        isReload: false,
      };
    }
    return undefined;
  }

  /**
   * 获取tsc的寻址路径，后续会被设置到tsc的compilerOptions中
   * @param basePath
   * @private
   */
  protected getTscAddressPaths(basePath: string) {
    const pkgNameToPkgBriefInfo = this.pkgNameToPkgBriefInfo;
    const paths: Record<string, string[]> = {};
    Object.values(pkgNameToPkgBriefInfo).forEach((pkgInfo) => {
      const sourceRoots = pkgInfo.sourceRoots?.map((sourceRoot) => {
        return path.join(path.relative(basePath, path.resolve(pkgInfo.pkgRoot, sourceRoot)), '*');
      });
      if (sourceRoots?.length) {
        paths[`${pkgInfo.pkgName}/*`] = sourceRoots;
      }
    });
    return paths;
  }

  protected async initDefaultArkCompileConfig(): Promise<ProjectConfig> {
    const mockConfigPath = this.targetData.getModuleModel().getMockConfigPath();
    const projectConfig: ProjectConfig = {
      ...(await super.initDefaultArkCompileConfig()),
      pkgNameToPkgBriefInfo: this.pkgNameToPkgBriefInfo,
      projectModel: GlobalProjectDataService.getInstance().getCompileProjectModel(),
      pkgJsonFileHash: GlobalProjectDataService.getInstance().getProjectPkgJsonFileHash(),
      allModulePaths: GlobalProjectDataService.getInstance().getAllModulePaths(),
      routerMap: this.getRouterMapConfig(),
      obfuscationOptions: this.obfuscationOptions,
      byteCodeHarToDependencyKeys: this.byteCodeHarToDependencyKeys,
      compileBlockPkg: this.getBlockDependencies().map((value) => value.getDependencyName()),
      mockParams: {
        decorator: '@MockSetup',
        packageName: '@ohos/hamock',
        etsSourceRootPath: 'src/main/ets',
        mockConfigPath: fse.existsSync(mockConfigPath) ? mockConfigPath : undefined,
        mockConfigKey2ModuleInfo: getMockConfigKey2ModuleInfoMap(this.moduleModel.getMockConfigPath(), this.service),
      },
      caseSensitiveCheck: isWindows() || isMac() ? this.targetService.getBuildOption()?.strictMode?.caseSensitiveCheck ?? false : false,
      // 将用户配置的copyCodeResource添加到projectConfig编译初始化信息中，仅在DefaultArkCompileConfig中初始化，enable开关默认为true
      copyCodeResourceEnable: this.targetService.getBuildOption()?.resOptions?.copyCodeResource?.enable ?? true,
      copyCodeResourceExcludes: this.targetService.getBuildOption()?.resOptions?.copyCodeResource?.excludes ?? [],
      uiTransformOptimization: getOhosUiTransformOptimization(this.targetService),
      // sdk路径
      sdkPath: this.sdkInfo.sdkDir,
      useSourceHash: HvigorConfigLoader.getInstance().getPropertiesConfigValue(CommonConst.OHOS_ROLLUP_CACHE_USE_SOURCE_HASH) === true,
      localModuleParamMap: this.localModuleParamMap
    };

    // 编译ets时如果没有ets目录则默认传递一个ets目录，确保index中的入口路径可编译
    if (!projectConfig.aceModuleRoot) {
      projectConfig.aceModuleRoot = path.resolve(this.pathInfo.getModuleSrcMainPath(), this.codeType);
    }
    projectConfig.otherPaths = this.getTscAddressPaths(projectConfig.aceModuleRoot);
    projectConfig.collectImportersConfig = this.getCollectImportersConfig();

    return projectConfig;
  }

  private async initWidgetDefaultArkCompileConfig(): Promise<ProjectConfig> {
    const widgetCacheDir = this.getTaskTempDir(this.targetData, '_widget');
    const config: ProjectConfig = {
      ...(await this.initDefaultArkCompileConfig()),
      widgetCompile: 'true',
      cachePath: widgetCacheDir, // widget缓存和页面缓存不可复用
      aceBuildJson: this.pathInfo.getWidgetLoaderJsonPath(), // 区分loader.json
      byteCodeHar2HspDep: this.service.getByteCodeHar2Hsp(),
    };
    if (config.obfuscationOptions) {
      config.obfuscationOptions = {
        ...config.obfuscationOptions,
        obfuscationCacheDir: path.resolve(widgetCacheDir, 'obfuscation'),
      };
    }
    return config;
  }

  /**
   * routerMap缓存配置 <pageSourceFile:name1@buildFunction1#name2@buildFunction2...>
   * @private
   */
  private getRouterMapConfig() {
    const routerMapConfig = new Map<string, string>();
    const allDependencyRouterMapObjList: RouterMapOptions.RouterMapObj[] | undefined = [];
    this.service.getHarDependencies().forEach((dependency) => {
      if (dependency.isLocal()) {
        const localDependencyRouterMapObjList = getLocalDependencyRouterMap(this.projectModel, this.targetService, dependency);
        if (localDependencyRouterMapObjList) {
          Array.prototype.push.apply(allDependencyRouterMapObjList, localDependencyRouterMapObjList);
        }
      } else {
        const remoteDependencyRouterMapObjList = DependencyManager.getRemoteDependencyRouterMapObjList(dependency);
        if (remoteDependencyRouterMapObjList) {
          Array.prototype.push.apply(allDependencyRouterMapObjList, remoteDependencyRouterMapObjList);
        }
      }
    });

    const targetRouterMapObjList = this.targetService.getTargetRouterMapObjList(this.moduleModel) ?? allDependencyRouterMapObjList;
    targetRouterMapObjList
      ?.sort((routerMapObj1, routerMapObj2) => routerMapObj1.name.localeCompare(routerMapObj2.name))
      .forEach((routerMapObj: RouterMapObj) => {
        const pageSourceFile = path.resolve(routerMapObj.moduleNodeDir!, routerMapObj.pageSourceFile);
        const nameAndBuildFunction = `${routerMapObj.name}@${routerMapObj.buildFunction}`;
        const nameAndBuildFunctionValue = routerMapConfig.has(pageSourceFile)
          ? `${routerMapConfig.get(pageSourceFile)}#${nameAndBuildFunction}`
          : nameAndBuildFunction;
        routerMapConfig.set(pageSourceFile, nameAndBuildFunctionValue);
      });

    return routerMapConfig;
  }

  protected arkDataCompile() {
    const harDependencies = this._taskService.getModuleService().getHarDependencies();
    if (harDependencies === undefined || harDependencies.length <= 0) {
      return;
    }

    // 获取当前模块下build产物中arkdata目录中所有json5文件
    const mainModuleName = this._taskService.getModuleService().getModuleModel().getName();
    const mainModuleArkdataPath = this._taskService.getTargetData().getPathInfo().getModuleSrcMainResourceRawfileArkdataPath();
    const mainModuleBuildArkdataPath = this._taskService.getTargetData().getPathInfo().getIntermediatesResArkDataPath();
    this.arkdataPathAll.push(mainModuleArkdataPath);
    const arkDataJsonSet: Set<string> = new Set();
    if (fse.existsSync(mainModuleArkdataPath)) {
      const mainArkDataFiles = fse.readdirSync(mainModuleArkdataPath);
      for (const mainArkDataFile of mainArkDataFiles) {
        arkDataJsonSet.add(mainArkDataFile);
      }
    }
    if (!fse.existsSync(mainModuleBuildArkdataPath)) {
      fse.mkdirSync(mainModuleBuildArkdataPath, { recursive: true });
    }

    for (const harDependence of harDependencies) {
      const packageName = harDependence.getPackageName();
      const moduleModel = this._taskService.getModuleService().getProjectModel().getModuleModelByName(packageName);
      const profilePath = moduleModel?.getProjectDir();
      if (profilePath !== undefined) {
        const harModuleArkDataPath = path.resolve(
          profilePath,
          BuildDirConst.SRC,
          BuildDirConst.MAIN,
          BuildDirConst.RESOURCES,
          BuildDirConst.RAW_FILE,
          BuildDirConst.ARK_DATA,
          BuildDirConst.SCHEMA,
        );
        if (!fse.existsSync(harModuleArkDataPath)) {
          continue;
        }
        this.arkdataPathAll.push(harModuleArkDataPath);
        const curModuleArkDataFiles = fse.readdirSync(harModuleArkDataPath);

        // 如果主模块下有同名json5文件，需要报错
        for (const curModuleArkDataFile of curModuleArkDataFiles) {
          if (arkDataJsonSet.has(curModuleArkDataFile)) {
            this._log.printErrorExit('SAME_NAME_ARK_DATA_JSON_FILE', [curModuleArkDataFile, packageName, curModuleArkDataFile, mainModuleName]);
          }
          arkDataJsonSet.add(curModuleArkDataFile);

          // 编到主模块下,当主模块build目录下不存在arkdata路径时，创建路径
          const curModuleArkDataFilePath = path.resolve(harModuleArkDataPath, curModuleArkDataFile);
          const destinationArkDataFilePath = path.resolve(mainModuleBuildArkdataPath, curModuleArkDataFile);
          if (fse.existsSync(mainModuleBuildArkdataPath)) {
            fse.copyFileSync(curModuleArkDataFilePath, destinationArkDataFilePath);
          }
        }
      }
    }
  }

  initTaskDepends(): void {
    if (limitMinApiVersion(this.targetData, ApiVersion.API_VERSION_9)) {
      this.declareDependsList(GenerateLoaderJson.name, CompileResource.name, CommonTask.CREATE_BUILD_PROFILE.name);

      // 如果hap/hsp引用了本地har模块则额外调用har模块的CREATE_HAR_BUILD_PROFILE任务用于生成BuildProfile.ets文件
      const depHarTargets = HarTargetUtil.calDepHarTargets(this.targetService);
      depHarTargets.forEach((targetName, harName) => this.declareDepends(`${targetName}@${CommonTask.CREATE_HAR_BUILD_PROFILE.name}`, harName));
    } else {
      this.declareDependsList(GenerateLoaderJson.name, CompileResource.name);
    }
  }

  /**
   * 获取传递给etsLoader的ark混淆配置
   * API10以上stage模型release模式可用
   *
   * selfConfig
   * dependencies
   * sdkApis
   * obfuscationCacheDir
   * exportRulePath
   *
   * @return ObfuscationOptions
   * @return undefined
   */
  async getObfuscationOptions(): Promise<ObfuscationOptions | undefined> {
    const buildOpt: BuildOpt = this.targetService.getBuildOption();
    if (buildOpt.debuggable) {
      hvigorCore.getExtraConfig().get(InjectConst.ARK_OBFUSCATION)?.toLowerCase() === 'true' && this._log.warn('Obfuscation is only enabled in release mode.');
      return undefined;
    }
    const cacheDir: string = path.resolve(this.getTaskTempDir(this.targetData), 'obfuscation');
    let obfuscation: Obfuscation | undefined = buildOpt.arkOptions?.obfuscation;
    if (buildOpt.artifactType) {
      this._log.warn('buildOption/artifactType is deprecated, please use buildOption/arkOptions/obfuscation instead.');
    }
    if (buildOpt.artifactType === ArtifactType.OBFUSCATION && obfuscation?.ruleOptions?.enable === undefined) {
      obfuscation = { ...obfuscation, ruleOptions: { enable: true }, libDir: this.module.getNodeDir() };
    }

    const obfuscationEnable = this.getObfuscationEnable();
    if (!obfuscation || obfuscation.ruleOptions?.enable === undefined) {
      if (obfuscationEnable === undefined) {
        return undefined;
      }
      this._log.debug(`Command line specified enable was used, obfuscationEnable is: ${obfuscationEnable}`);
      obfuscation = { ruleOptions: { enable: obfuscationEnable }, libDir: this.module.getNodeDir() };
    }
    if (this.compileApiVersion < ApiVersion.API_VERSION_10 || this.isFaMode) {
      this._log.warn(`Obfuscation is supported in stage mode of API${ApiVersion.API_VERSION_10} or later.`);
      return undefined;
    }
    if (this.moduleModel.isHapModule() && obfuscation.consumerFiles !== undefined) {
      this._log.warn("The property 'consumerFiles' is not support in hap module´s build-profile.json5");
    }
    if (obfuscationEnable !== undefined && obfuscation.ruleOptions) {
      obfuscation.ruleOptions.enable = obfuscationEnable;
    }
    if (hvigorCore.getParameter().getProperty('ohos.obfuscationRules.optimization') === true) {
      try {
        return getObfuscationOptionsWithCache(this.targetService, obfuscation, cacheDir);
      } catch (e) {
        return resolveObfuscationOptions(this.targetService, obfuscation, cacheDir);
      }
    }
    return resolveObfuscationOptions(this.targetService, obfuscation, cacheDir);
  }

  computeObfuscationFileHash(obfuscationOptions: ObfuscationOptions) {
    // 收集所有混淆配置文件
    const rules: string[] = [];
    rules.push(...(obfuscationOptions.selfConfig?.ruleOptions?.rules ?? []));
    rules.push(...(obfuscationOptions.selfConfig?.consumerRules ?? []));
    rules.push(...obfuscationOptions.dependencies.hars);
    rules.push(...obfuscationOptions.dependencies.hsps);
    for (const obfuscation of [...obfuscationOptions.dependencies.libraries, ...obfuscationOptions.dependencies.hspLibraries]) {
      rules.push(...(obfuscation.ruleOptions?.rules ?? []));
      rules.push(...(obfuscation.consumerRules ?? []));
    }
    // 计算所有文件的hash
    const hasher: Hash = crypto.createHash('sha256');
    for (const file of rules) {
      if (fse.existsSync(file)) {
        hasher.update(fse.readFileSync(file));
      }
    }
    return hasher.digest('hex');
  }

  protected getIncrementalCacheItem(key: string): TaskInputValue | undefined {
    return ProjectCacheService.getInstance(this.projectModel.getProject())
      .getTaskSnapShot(this.getName(), this.module)
      ?.getInputs()
      ?.find((input) => input.getName() === key)
      ?.getValue();
  }

  /**
   * 清除arkts代码缓存目录build/${product}/cache/${target}/default@CompileArkTS/esmodule/${buildMode}
   *
   * @protected
   */
  protected async cleanCache() {
    const cachePath: string = this.getTaskTempDir(this.targetData);
    await fse.emptydir(cachePath);
    const tsCheckCache: string = path.resolve(cachePath, '..', '.ts_checker_cache');
    fse.existsSync(tsCheckCache) && (await fse.rm(tsCheckCache));
  }

  private getObfuscationEnable() {
    const arkObfuscation = hvigorCore.getExtraConfig().get(InjectConst.ARK_OBFUSCATION)?.toLowerCase();
    if (arkObfuscation === 'false') {
      return false;
    } else if (arkObfuscation === 'true') {
      return true;
    }

    return this.buildOption?.arkOptions?.obfuscation?.ruleOptions?.enable;
  }

  /**
   * 检查oh-package.json5文件的main字段是否配置且正确
   *
   * @private
   */
  private checkPackageMain() {
    const main = !this.packageJsonObj.main ? BuildArtifactConst.INDEX_ETS : this.packageJsonObj.main;
    return fse.existsSync(path.resolve(this.pathInfo.getModulePath(), main));
  }

  /**
   * 检查RollUpPluginInEtsLoader是否存在
   *
   * @private
   */
  private checkRollUpPlugin() {
    return this.codeType === CodeType.ETS ? this.sdkInfo.hasRollUpPluginInEtsLoader : this.sdkInfo.hasRollUpPluginInJsLoader;
  }

  onFailed(error: Error) {
    super.onFailed(error);
    const extendedErrorInfo: ExtendedErrorInfo = error as ExtendedErrorInfo;
    if (extendedErrorInfo.extraInfo) {
      extendedErrorInfo.extraInfo.infoMsg && this._log.info(extendedErrorInfo.extraInfo.infoMsg);
      extendedErrorInfo.extraInfo.warnMsg && this._log.warn(extendedErrorInfo.extraInfo.warnMsg);
    }
  }
}
